package com.system.core.plugins.mybatis;

import java.io.ByteArrayInputStream;
import java.nio.ByteBuffer;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.PrecisionModel;
import org.locationtech.jts.io.InputStreamInStream;
import org.locationtech.jts.io.WKBReader;

import lombok.SneakyThrows;

@MappedTypes(LineString.class)
@MappedJdbcTypes(JdbcType.OTHER)
public class MysqlGeometryTypeHandler extends BaseTypeHandler<Geometry> {

	@Override
	public void setNonNullParameter(PreparedStatement ps, int i, Geometry parameter, JdbcType jdbcType) throws SQLException {
        Connection conn = ps.getConnection();

        String wkt = parameter.toText();
        String sql = "SELECT ST_GeomFromText(?)";

        try (PreparedStatement stmt = conn.prepareStatement(sql)) {
            stmt.setString(1, wkt);
            ResultSet rs = stmt.executeQuery();

            if (rs.next()) {
                Object obj = rs.getObject(1);
                ps.setObject(i, obj);
            } else {
                throw new SQLException("Failed to convert Geometry to database type");
            }
        }
    }

	@Override
	public Geometry getNullableResult(ResultSet rs, String columnName) throws SQLException {
		final Object object = rs.getObject(columnName);
        return getGeometry(object);
	}
	
	@SneakyThrows
    private Geometry getGeometry(Object object) {
        ByteArrayInputStream inputStream = new ByteArrayInputStream((byte[]) object);
        byte[] sridBytes = new byte[4];
        inputStream.read(sridBytes);
        int srid = ByteBuffer.wrap(sridBytes).getInt();
        WKBReader wkbReader = new WKBReader(new GeometryFactory(new PrecisionModel(), srid));
        return wkbReader.read(new InputStreamInStream(inputStream));
    }

	@Override
	public Geometry getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
		final Object object = rs.getObject(columnIndex);
        return getGeometry(object);
	}

	@Override
	public Geometry getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
		final Object object = cs.getObject(columnIndex);
        return getGeometry(object);
	}
}